home *** CD-ROM | disk | FTP | other *** search
- // CNTRLR.CPP - "driver" function for RARS - M. Timin, March, 1995
- // adapted to ver. 0.4 3/12/95 by M. Timin
-
- /* Commented Robot Driver */
- /*
- This robot driver calculates a cornering speed for each corner based on its
- radius. He accelerates on each straightaway until a certain fraction
- of its length is reached; Then he slows down, attempting to arrive at
- the corner with the proper cornering speed. In the corner he attempts
- to maintain the cornering speed while attempting to stay in the middle
- of the track. His strategy for passing is to choose right or left at
- random, and then throw the car into a sharp slide toward that direction.
- */
-
- #include <string.h>
- #include <stdlib.h>
- #include <math.h>
- #include "car.h"
-
- extern char* glob_name; // The name string, below, will be copied here
-
- // This structure will be built in the data area provided by the caller:
- struct params {
- double CORN_SPD_CON; // determines how fast to take corners
- double STEER_GAIN; // servo gain, for staying in "lane"
- double STEER_DAMP; // servo damping, to prevent "weaving"
- double END_ACCEL; // we accelerate until this fraction of length
- double END_CORNER;// used to decide when to start leaving the corer
- double SLIP_LIM; // maximum wheel slip, ft/sec, in wheel_slip()
- double SLIP_CON;
- double BIAS;
- double NEAR_END;
- double SHARP_TURN; // change in alpha when attempting to pass
- int PASSING_TIME; // time to stay in passing maneuver, counts
- };
-
- inline double ABS(double arg) { return arg < 0.0 ? -arg : arg; }
-
- // The following function calculates the speed for a corner.
- // The lateral force produced by cornering is proportional to the square
- // of the speed, and is inversely proportional to the radius of the path.
- // Therefore, the attainable cornering speed for differnt radii is
- // proportional to the square root of the radius. This function implements
- // that rule. The value to use for CORN_SPD_CON can be determined by
- // trial and error. Example result: if the car is following a path with
- // radius of 100 ft, and if CORN_SPD_CON is 5.0, then corn_speed is 50 ft/sec.
- double corn_speed(double radius, double param)
- {
- if(radius < 0.0) // change sign of negative radius
- radius = -radius;
- else if(radius == 0.0) // This is just insurance, this funtion doesn't
- return(200.0); // make sense when the radius is zero.
- return param * sqrt(radius+15.0);
- }
-
- // In order to set vc, if you know how fast you want to go (goal), and how
- // fast you are going now (present), This function will compute a reasonable
- // value for vc. The value is never very far from the present speed, both
- // to attempt to stay within the power limit, and to maintain steering control.
- // You can adjust the resulting slip by changing "param".
- double wheel_speed(double goal, double present, double param)
- {
- double ws;
-
- if(present > goal + 2 * param) // if too fast,
- ws = present - param; // slow down.
- else if(present < goal - 2 * param) // if too slow,
- ws = present + param; // accelerate.
- else // if quite close,
- ws = (goal + present) / 2; // approach desired speed gently.
-
- return ws;
- }
-
- /* These two structures from CAR.H are repeated here as comments, because
- the "driver" function receives situation as input and produces con_vec
- as output.
- struct situation { // a car's local situation as seen by the driver
- double cur_rad; // radius of inner wall of curve (0 means straight)
- double cur_len; // length of current track segment (angle if curve)
- double to_lft; // distance to left wall
- double to_rgt; // distance to right wall
- double to_end; // how far to end of current track seg. (angle or feet)
- double v; // the speed of the car, feet per second
- double vn; // component of v perpendicular to track direction
- double nex_len; // length of the next track segment (angle if curve)
- double nex_rad; // radius of inner wall of next segment (or 0)
- double after_rad; // radius of the segment after that one. (or 0)
- double power_req; // ratio: power requested by driver to maximum power
- int dead_ahead; // set when there is a car dead ahead, else 0
- int backward; // set if cars motion is opposed to track direction
- rel_state* nearby; // relative states of three cars in front of you
- void* data_ptr; // pointer to driver's scratchpad RAM area
- };
-
- struct con_vec { double alpha, vc; }; // control vector, steering & throttle
- */
-
- // The task of this function is to compute vc and alpha. A high speed
- // car on a track is a little like the keel of a boat; if you set the keel
- // at a slight angle to the direction of the oncoming water, you get a large
- // force to the side. That is how we corner the car. The driver sets the
- // car at a slight angle with respect to its direction of motion, this
- // cause a force to the side, causing the path of the car to curve. The
- // magnitude of the force is proportional to the angle (alpha) for very
- // small alpha, and when there is not much wheel spin. The wheel spin
- // is controlled by vc, which is the rearward speed of the bottom of the
- // tire. When going down the straight at a constant, moderate velocity,
- // then vc is equal to the speed of the car. For acceleration, vc is
- // made a little greater than the speed. For braking, it is made a little
- // less. When accelerating, vc is limited by the power available.
- con_vec cntrlR(situation s)
- {
- const char name[] = "Rudy"; // This is the robot driver's name!
- static int init_flag = 1; // cleared by first call
- double speed; // target speed for cornering, ft/sec
- double speed_next; // target speed for next corner
- con_vec result; // This is what is returned.
- double width; // track width, feet
- double alpha, vc; // components of result
- static double alpha_inc = 0.0; // alpha increment during passing maneuver
- static int counting = 0; // will be set and counting down when passing
- params* p_ptr;
-
- // This paragraph has nothing to do with car control; it is just
- // to identify the driver by copying its name to a global RAM area:
- // This happens only on the very first call to this function
-
- p_ptr = (params*)s.data_ptr; // point to the data area
-
- if(init_flag == 1) { // first time only, copy name:
- strcpy(glob_name, name);
- init_flag = 0;
- result.alpha = result.vc = 0;
- return result;
- }
-
- if(s.starting) { // one time only, set parameter values:
- // These parameters may be adjusted to get better performance:
- p_ptr->CORN_SPD_CON = 7.50; // determines how fast to take corners
- p_ptr->STEER_GAIN = 1.1; // servo gain, for staying in "lane"
- p_ptr->STEER_DAMP = 0.8; // servo damping, to prevent "weaving"
- p_ptr->END_ACCEL = .30; // we accelerate until this fraction of length
- p_ptr->END_CORNER = 2.5;// used to decide when to start leaving the corer
- p_ptr->SLIP_LIM = 5.0; // maximum wheel slip, ft/sec, in wheel_slip()
- p_ptr->SLIP_CON = 500.0; //
- p_ptr->BIAS = .05; // cornering estimated alpha
- p_ptr->NEAR_END = 1.8; // widths from corner to start with BIAS
- p_ptr->SHARP_TURN = .07; // change in alpha when attempting to pass
- p_ptr->PASSING_TIME = 90; // time to stay in passing maneuver, counts
- result.alpha = 0.0; result.vc = s.v + 40; // accelerate, full power!
- return result;
- }
-
- if(stuck(s.backward, s.v,s.vn, s.to_lft,s.to_rgt, &result.alpha,&result.vc))
- return result;
-
- // Set alpha based on a servo-mechanism approach, trying to stay
- // in the middle of the track, i.e., s.to_left equal to .5 * width:
- width = s.to_lft + s.to_rgt; // find width of track
- if(s.cur_rad > 0) {
- alpha = p_ptr->STEER_GAIN * (s.to_lft - .15 * width) / width;
- alpha += p_ptr->BIAS;
- }
- else if(s.cur_rad < 0) {
- alpha = p_ptr->STEER_GAIN * (s.to_lft - .85 * width) / width;
- alpha -= p_ptr->BIAS;
- }
- else { // on straightaway
- alpha = .2 * p_ptr->STEER_GAIN * (s.to_lft - .58 * width) / width;
- if(s.to_end < p_ptr->NEAR_END * width)
- if(s.nex_rad > 0)
- alpha += p_ptr->BIAS;
- else if(s.nex_rad < 0)
- alpha -= p_ptr->BIAS;
- }
- alpha -= p_ptr->STEER_DAMP * s.vn / s.v; // This is damping, to prevent oscillation
-
- // calculate target speeds for current corner and the next:
- speed = corn_speed(s.cur_rad, p_ptr->CORN_SPD_CON);
- speed_next = corn_speed(s.nex_rad, p_ptr->CORN_SPD_CON);
-
- // now set the tire speed, vc:
- if(s.cur_rad == 0.0) // If we are on a straightaway,
- if(s.to_end > p_ptr->END_ACCEL * s.cur_len) // if we are far from the end,
- vc = s.v + p_ptr->SLIP_CON / s.v; // keep accellerating near full power
- else // otherwise,
- vc = wheel_speed(speed_next, s.v, p_ptr->SLIP_LIM); // brake for next corner
- else // If we're in the curve, maintain speed.
- if(s.to_end*ABS(s.cur_rad) > p_ptr->END_CORNER * width*s.v/speed_next)
- // if we are far from the next corner, stay at "speed".
- vc = wheel_speed(speed, s.v, p_ptr->SLIP_LIM);
- else { // but when we near the next corner, adjust to "speed_next"
- vc = wheel_speed(speed_next, s.v, p_ptr->SLIP_LIM);
- alpha += s.cur_rad > 0 ? p_ptr->BIAS : -p_ptr->BIAS;
- }
-
-
- // The passing maneuver:
- if(s.dead_ahead & !counting) { // When first encountering the car ahead:
- counting = p_ptr->PASSING_TIME; // setup the timer,
- if(random(29999) < 15000) // choose a right or left maneuver:
- alpha_inc = p_ptr->SHARP_TURN;
- else
- alpha_inc = -p_ptr->SHARP_TURN;
- }
- if(counting) { // If we are still in the passing maneuver,
- alpha += alpha_inc; // change alpha
- --counting; // count down to zero
- }
-
- result.vc = vc; result.alpha = alpha;
- return result;
- }
-